﻿using System.Collections.Generic;
using NHibernate;
using NHibernate.Cfg;
using System;
using Framework.Repositories.NHibernate;
using Framework.Repositories;

namespace Framework.Infrastructure.Context.NHibernate
{
    /// <summary>
    /// Represents the repository context which supports NHibernate implementation.
    /// </summary>
    public class NHibernateContext : IDisposable
    {
        #region Private Fields
        private volatile bool committed = true;
        private readonly DatabaseSessionFactory databaseSessionFactory;
        private readonly ISession session = null;
        //private readonly ISpecificationParser<ICriteria> specificationParser = null;
        private readonly Dictionary<string, object> repositories = new Dictionary<string, object>();
        private readonly List<object> newObjects = new List<object>();
        private readonly List<object> modifiedObjects = new List<object>();
        private readonly List<object> deletedObjects = new List<object>();
        private readonly object sync = new object();
        #endregion

        #region Ctor
        /// <summary>
        /// Initializes a new instance of <c>NHibernateContext</c> class.
        /// </summary>
        public NHibernateContext(Configuration nhibernateConfig)
        {
            databaseSessionFactory = new DatabaseSessionFactory(nhibernateConfig);
            session = databaseSessionFactory.Session;
            //specificationParser = new NHibernateSpecificationParser(session);
        }
        #endregion

        #region Protected Methods
        /// <summary>
        /// Disposes the object.
        /// </summary>
        /// <param name="disposing">A <see cref="System.Boolean"/> value which indicates whether
        /// the object should be disposed explicitly.</param>
        protected void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (!committed)
                    Commit();
                ISession dbSession = session;
                if (dbSession != null && dbSession.IsOpen)
                {
                    dbSession.Close();
                }
                dbSession.Dispose();
            }
        }
        #endregion

        #region Public Properties
        /// <summary>
        /// Gets the instance of the NHibernate Session object.
        /// </summary>
        public ISession Session { get { return session; } }
        ///// <summary>
        ///// Gets the instance of the specification parser object.
        ///// </summary>
        //public ISpecificationParser<ICriteria> SpecificationParser { get { return specificationParser; } }
        #endregion

        #region IRepositoryContext Members
        /// <summary>
        /// Registers a new object to the context.
        /// </summary>
        /// <param name="obj">The object that is going to be added.</param>
        public void RegisterNew(object obj)
        {
            newObjects.Add(obj);
            committed = false;
        }
        /// <summary>
        /// Registers a modified object on the context.
        /// </summary>
        /// <param name="obj">The object that is going to be modified.</param>
        public void RegisterModified(object obj)
        {
            modifiedObjects.Add(obj);
            committed = false;
        }
        /// <summary>
        /// Registers a deleted object on the context.
        /// </summary>
        /// <param name="obj">The object that is going to be deleted.</param>
        public void RegisterDeleted(object obj)
        {
            deletedObjects.Add(obj);
            committed = false;
        }
        /// <summary>
        /// Gets the repository instance from the transaction context according
        /// to the given aggregate root type.
        /// </summary>
        /// <typeparam name="TAggregateRoot">The type of the aggregate root.</typeparam>
        /// <returns>The repository instance.</returns>
       #endregion

        #region IUnitOfWork Members
        /// <summary>
        /// Gets a <see cref="System.Boolean"/> value which indicates
        /// whether the Unit of Work could support Microsoft Distributed
        /// Transaction Coordinator (MS-DTC).
        /// </summary>
        public bool DTCompatible
        {
            // TODO: To be determined.
            get { return false; }
        }
        /// <summary>
        /// Gets a <see cref="System.Boolean"/> value which indicates
        /// whether the Unit of Work was successfully committed.
        /// </summary>
        public bool Committed
        {
            get { return this.committed; }
        }
        /// <summary>
        /// Commits the transaction.
        /// </summary>
        public void Commit()
        {
            using (ITransaction transaction = session.BeginTransaction())
            {
                try
                {
                    foreach (object obj in newObjects)
                    {
                        session.Save(obj);
                    }
                    foreach (object obj in modifiedObjects)
                    {
                        session.SaveOrUpdate(obj);
                    }
                    foreach (object obj in deletedObjects)
                    {
                        session.Delete(obj);
                    }
                    transaction.Commit();
                    newObjects.Clear();
                    deletedObjects.Clear();
                    committed = true;
                }
                catch
                {
                    transaction.Rollback();
                    throw;
                }
            }
        }
        /// <summary>
        /// Rollback the transaction.
        /// </summary>
        public void Rollback()
        {
            committed = false;
        }

        #endregion


        #region IDisposable Members

        public void Dispose()
        {
            session.Dispose();
        }

        #endregion
    }
}
